React의 useContext를 이용 시 리 렌더링을 막는 방법
최근 useContext
를 사용 중 불필요한 리 렌더링을 막는 방법에 관해서 관심이 생겨 직접 확인 해 보았습니다.
준비
먼저 확인을 위해 샘플 코드를 만들어 보겠습니다.
1.먼저 react 프로젝트를 생성합니다.
npm create vite@latest [project-name] -- --template react-ts
2.확인을 위해 관련 component와 context 코드를 작성합니다.
countContext.tsx
import { createContext, useContext } from 'react'; export interface CountContextType { count: number countIncrement : () => void countDecrement : () => void } export const CountContext = createContext<CountContextType | undefined>(undefined); export const useCountContext = (): CountContextType => { const context = useContext(CountContext); if (context === undefined) { throw new Error('must used within CountProvider'); } return context; };
Main.tsx
import { useCountContext } from "../countContexts" export function Main() { const { count, countIncrement, countDecrement } = useCountContext() return ( <div> <div>{count}</div> <button onClick={countIncrement}>+</button> <button onClick={countDecrement}>-</button> </div> ) }
Header.tsx
export function Header() { return ( <div>HEADER</div> ) }
Footer.tsx
export function Footer() { return ( <div>FOOTER</div> ) }
App.tsx
import { useCallback, useState } from "react" import { Header } from "./components/Header" import { Footer} from "./components/Footer" import { Main } from './components/Main' import { CountContext } from "./countContexts" function App() { const [count, setCount] = useState(0) const countIncrement = useCallback(() => { setCount(count + 1) },[count]) const countDecrement = useCallback(() => { setCount(count - 1) },[count]) return ( <CountContext.Provider value={{ count, countIncrement, countDecrement }}> <Header /> <Main /> <Footer /> </CountContext.Provider>); } export default App
일단 베이스가 되는 코드를 작성했습니다.
위 코드를 실행해 보면useContext
를 이용하고 있는 Main
component뿐만 아닌 Footer
와 Header
component도 함께 리 렌더링 되는 것을 확인 할 수 있습니다.
chrome의 확장 도구인 react developer tools의 profiler를 통해 확인해 보면 아래와 같이 리 렌더링 결과를 확인 할 수 있습니다.
방법1
첫 번째 방법은 React.memo
를 이용하는 방법입니다.
React.memo
를 이용하면 부모 component가 리 렌더링 시 불필요한 리 렌더링을 막을 수 있습니다.
보통 부모 component에서 넘겨주는 props의 변경이 없는 경우 React.memo
를 사용하면 불필요한 리 렌더링을 막을 수 있습니다.
이번 Hedaer
component의 경우에는 아무 props도 넘겨주지 않았기 때문에 리 렌더링이 불필요한 상태입니다.
그럼, 이전 코드의 Header
코드를 수정해 보겠습니다.
Header.tsx
import React from "react" export const Header = React.memo(() =>{ return ( <div>HEADER</div> ) })
위와 같이 코드를 작성 후 profiler에서 결과를 확인하면 아래와 같이 Header
component의 경우 리 렌더링이 발생하지 않는 것을 확인 할 수 있습니다.
방법2
두 번째 방법은 별도의 component 안에서 상태 값을 관리하여, App
component의 리 렌더링을 막으며,
하위 component 중 useContext
를 이용하고 있는 component만 리 렌더링시키는 방법입니다.
그럼, 바로 코드를 작성해 보겠습니다.
countContext.tsx
... export const CountProvider = ({ children, }: PropsWithChildren) => { const [count, setCount] = useState(0) const countIncrement = useCallback(() => { setCount(count + 1) },[count]) const countDecrement = useCallback(() => { setCount(count - 1) },[count]) return <CountContext.Provider value={{ count, countIncrement, countDecrement }}>{children}</CountContext.Provider>; };
App.tsx
... function App() { return ( <CountProvider> <Header /> <Main /> <Footer /> </CountProvider> ) } ...
위 코드를 작성 후 실행해 보면 아래와 같이 useContext
를 이용하고 있는 Main
component 이외의 component가 리 렌더링 되지 않는 것을 확인 할 수 있습니다.
보충
이외에도 공식 문서를 보면 useMemo
를 이용한 방법도 소개하고 있습니다만, 개인적으로 심플 하다고 생각하는 두 가지 방법만 확인해 보았습니다.
관심이 있으시다면 다른 여러 방법을 확인 후 사용하고 싶은 방법을 이용해 보셔도 좋을 듯합니다.
참고자료
react developer tools
react useContext - Optimizing re-renders when passing objects and functions